#ifndef SQLDATABASEMANAGER_H
#define SQLDATABASEMANAGER_H

#include <QObject>
#include <QSqlDatabase>
#include <QMap>
#include "../XXqtDefine.h"
#include "../Utility/Mapion.h"


/**
 * @brief The SqlDatabaseManager class
 *
 * TODO: 由于一些调整，形参的顺序后续需要调整，tableName需要是第一形参。
 */
class SqlDatabaseManager : public QObject
{
    Q_OBJECT
public:    
    enum Relation{
        Equal,
        MoreThan,
        MoreThanAndEqual,
        LessThan,
        LessThanAndEqual,
        NotEqual,
    };

    explicit SqlDatabaseManager(QObject *parent = nullptr);
    virtual ~SqlDatabaseManager();

    // [-] version
    /**
     * @brief getGlobalVersion 获取全局版本，默认是0。
     */
    static uint getGlobalVersion();
    /**
     * @brief setGlobalVersion 设置全局版本，默认是0，每次程序启动只能设置一次。
     * @param version
     */
    static void setGlobalVersion(uint version);
    // [!] version

    // [-] 初始化数据库
    /**
     * @brief init 创建并初始化数据库。
     * @param path 数据库路径。
     * @param tableInfo 表的参数。
     * {
     *  xxx:{k1:t1,k2:t2},
     *  xxx:{k1:t1,k2:t2},
     *  xxx:{k1:t1,k2:t2},
     * }
     */
    static void init(const QString &path, const QVariantMap &tableInfo);
    /**
     * @brief init 创建并初始化数据库。
     * @param path 数据库路径。
     * @param tableNameToMetaObject key为数据库中的表名，value是模型的元对象，
     * 根据目标元对象的属性列表生成数据库中的column，属性中需要将isUser设置为true，
     * 暂时只支持QString和int类型。
     */
    static void init(const QString &path, QMap<QString,QMetaObject> tableNameToMetaObject);
    /**
     * @brief init 创建并初始化数据库。
     * @param path 数据库路径。
     * @param metaObjects 模型的元对象列表，根据元对象classInfo中key为%kTableName%的值为表名，
     * 属性列表生成数据库中的column，属性中需要将isUser设置为true，暂时只支持QString和int类型。
     */
    XX_KEY(kTableName)
    static void init(const QString &path, QList<QMetaObject> metaObjects);
    // [!] 初始化数据库

    // [-] 打开数据库
    /**
     * @brief 打开数据库，当前路径没有数据库文件时，创建一个新的数据库。
     * @param path 数据路径。
     * @param create 设置为true时，没有目标数据库将会创建一个新数据库。
     * @return 成功返回true。
     */
    bool open(const QString &path, bool create = true);
    // [!] 打开数据库

    // [-] 创建表
    /** 将会被私有化，请勿使用。 */
    XX_KEY(kTableMainKeyType)
    /**
     * @brief 创建表，并自动添加主键 id 。
     * @param keyAndType 表的列名称和类型，类型需要为QString。
     * @param tableName 表名称。
     * @return 成功返回true。
     */
    bool createTable(const QString &tableName, const QVariantMap &keyAndType);
    /**
     * @brief createTable 创建表，并自动添加主键 id 。
     * @param textKey 表的列名称，全部类型会被设置为 text。
     * @param tableName 表名称。
     * @return 成功返回true。
     */
    bool createTable(const QStringList &textKey, const QString &tableName);
    /**
     * @brief 创建数据库表，不会自动添加主键，废弃。
     */
    bool createTable(const QMap<QString,QString> &keyAndType, const QString &tableName);
    // [!] 创建表

    /**
     * @brief 查询数据库表是否存在
     */
    bool tableExisted(const QString &tableName);

    /**
     * @brief 查询数据库表中是否存在某字段
     */
    bool keyExisted(const QString &key, const QString &tableName);

    /**
     * @brief 插入一个字段到数据库表中
     * @param key 需要插入字段
     * @param type 字段对应的数据类型，考虑使用枚举，如：int、float、bool...
     * @param tableName 插入的目标数据库表名称
     * @return true:成功
     */
    bool addKey2Table(const QString &key, const QString &type, const QString &tableName);
    bool addKeys2Table(const QVariantMap &keyAndType, const QString &tableName);
    bool addKeys2Table(const QMap<QString,QString> &keyAndType, const QString &tableName);

    /**
     * @brief 从数据库表中删除字段（注意：sqlite不支持删除key，所以此函数直接返回false）
     * @param key 需要删除的字段
     * @param tableName 需要删除字段的数据库表名
     * @return true:成功
     */
    bool removeKeyAtTable(const QString &key, const QString &tableName);

    /**
     * 移除表数据
     */
    bool removeMapAtTable(const QVariantMap &equalTo, const QString &tableName);
    template<class T>
    bool removeMapionAtTable(const QVariantMap &equalTo){
        auto tableName = getTableNameAtMetaObject<T>();
        if(tableName.isEmpty()){
            return false;
        }
        return removeMapAtTable(equalTo,tableName);
    }

    /**
     * @brief 找出能够匹配的数据表，并对其进行值修改
     * @param values 需要修改的键值对
     * @param equalTo 需要匹配的值
     * @param tableName 数据库表名称
     * @return true:成功
     */
    bool setValues2Table(const QVariantMap &values, const QMap<QString,QVariant> &equalTo,const QString &tableName);

    /**
     * @brief 插入一个数据表
     * @param values 表中的所有内容
     * @param tableName 数据库
     * @return
     */
    bool addMap2Table(const QVariantMap &values, const QString &tableName);
    bool addMaps2Table(const QList<QVariantMap> &maps, const QString &tableName);
    template<class T>
    bool addMapion2Table(QString tableName, T mapion){
        auto map = mapion.toMap();
        return addMap2Table(map,tableName);
    }
    template<class T>
    bool addMapions2Table(QString tableName, QList<T> mapions){
        QList<QVariantMap> maps;
        for (auto iter=mapions.begin(); iter!=mapions.end(); iter++) {
            maps << iter->toMap();
        }
        return addMaps2Table(maps,tableName);
    }
    template<class T>
    bool addMapion2Table(T mapion){
        QString tableName = getTableNameAtMetaObject<T>();
        if(tableName.isEmpty()){
            return false;
        }

        auto map = mapion.toMap();
        return addMap2Table(map,tableName);
    }
    template<class T>
    bool addMapions2Table(QList<T> mapions){
        QString tableName = getTableNameAtMetaObject<T>();
        if(tableName.isEmpty()){
            return false;
        }

        QList<QVariantMap> maps;
        for (auto iter=mapions.begin(); iter!=mapions.end(); iter++) {
            maps << iter->toMap();
        }
        return addMaps2Table(maps,tableName);
    }

    // [-] get with equal
    /**
     * @brief getMapFromTable 按指定的排序获取匹配上键值的表内容
     * @param tableName 表名称
     * @param equelTo 需要匹配的值
     * @param orderBy 使用什么作为排序
     * @param ASC 是否为升序
     * @return
     */
    QList<QVariantMap> getMapFromTable(const QString &tableName, const QVariantMap &equelTo = QVariantMap(), const QStringList &orderBy=QStringList(), bool ASC = true);
    template<class T>
    QList<T> getMapionFromTable(const QString &tableName, const QVariantMap &equelTo = QVariantMap(), const QStringList &orderBy=QStringList(), bool ASC = true){
        QList<T> objs;
        auto maps = getMapFromTable(tableName,equelTo,orderBy,ASC);
        for (auto iter=maps.begin(); iter!=maps.end(); iter++) {
            T obj(*iter);
            objs << obj;
        }
        return objs;
    }
    template<class T>
    QList<T> getMapionFromTable(const QVariantMap &equelTo = QVariantMap(), const QStringList &orderBy=QStringList(), bool ASC = true){
        QString tableName = getTableNameAtMetaObject<T>();
        if(tableName.isEmpty()){
            return {};
        }
        return getMapionFromTable<T>(tableName,equelTo,orderBy,ASC);
    }
    // [!] get with equal

    // [-] get with relation
    QList<QVariantMap> getMapFromTableRelation(const QString &tableName, const QMap<QString,QPair<QVariant,Relation>> &relaTo={}, const QStringList &orderBy={}, bool ASC=true);
    template<class T>
    QList<T> getMapionFromTableRelation(const QString &tableName, const QMap<QString,QPair<QVariant,Relation>> &relaTo={}, const QStringList &orderBy=QStringList(), bool ASC = true){
        QList<T> objs;
        auto maps = getMapFromTableRelation(tableName,relaTo,orderBy,ASC);
        for (auto iter=maps.begin(); iter!=maps.end(); iter++) {
            T obj(*iter);
            objs << obj;
        }
        return objs;
    }
    template<class T>
    QList<T> getMapionFromTableRelation(const QMap<QString,QPair<QVariant,Relation>> &relaTo={}, const QStringList &orderBy=QStringList(), bool ASC = true){
        QString tableName = getTableNameAtMetaObject<T>();
        if(tableName.isEmpty()){
            return {};
        }
        return getMapionFromTableRelation<T>(tableName,relaTo,orderBy,ASC);
    }
    // [!] get with relation

    /**
     * @brief getValueMaxFromTable 获取表中某一健的最大值，暂时只针对int
     * @param tableName 表名称
     * @param key 需要执行的健
     * @return 返回当前出现的最大值
     */
    int getValueMaxFromTable(const QString &tableName, const QString &key);

    /**
     * @brief updateVersion 更新数据库的版本
     */
    void updateVersion(uint version);

    /**
     * @brief getVersion 获取数据库版本
     */
    int getVersion();

    /**
     * @brief isNewest 是否为最新版本，需要跟GlobalVersion进行比较
     */
    bool isNewest();

    /**
     * @brief 关闭数据库
     */
    bool close();

    /**
     * @brief 已经打开一个数据库
     */
    bool isOpen() const { return _isOpen; }

private:
    QString getTableNameAtMetaObject(QMetaObject metaObject);
    template<class T>
    QString getTableNameAtMetaObject(){
        return getTableNameAtMetaObject(T::staticMetaObject);
    }

private:
    bool _isOpen;
    QString _name;
    //QSqlDatabase _database;
    static uint _version;

public:
    QSqlDatabase _database;
};

#endif // SQLDATABASEMANAGER_H
